use axum::{
    Json,
    http::StatusCode,
    response::{IntoResponse, Response},
};
use sea_orm::rbac::schema;
use serde::{Deserialize, Serialize};
use serde_json::json;
use utoipa::ToSchema;

use crate::prelude::BizError;
use crate::prelude::BizResult;

#[derive(Debug, Serialize, Deserialize, Clone, ToSchema)]
#[schema(title = "ApiResponse", description = "API 响应结构")]
pub struct ApiResponse<T> {
    #[schema(example = 200, default = 200, nullable = false)]
    pub code: u32,

    #[schema(example = "success", default = "success", nullable = false)]
    pub message: String,

    #[serde(skip_serializing_if = "Option::is_none")]
    #[schema(example = "{id: 942945}", nullable = true)]
    pub data: Option<T>,

    #[serde(skip_serializing_if = "Option::is_none")]
    #[schema(example = "{name: 'login name is required'}", nullable = true)]
    pub errors: Option<serde_json::Value>,
}

impl<T> IntoResponse for ApiResponse<T>
where
    T: Serialize,
{
    fn into_response(self) -> Response {
        let response = json!(self);
        (StatusCode::OK, Json(response)).into_response()
    }
}

impl<T> ApiResponse<T> {
    pub fn new(code: u32, message: String, data: Option<T>) -> Self {
        Self {
            code,
            message,
            data,
            errors: None,
        }
    }

    pub fn response_ok(data: T) -> Self {
        Self::new(200, "success".into(), Some(data))
    }

    pub fn response_biz_error(err: BizError) -> Self {
        Self {
            code: err.http_code_u32(),
            data: None,
            message: err.to_string(),
            errors: err.detail_errors(),
        }
    }

    pub fn internal_server_error() -> Self {
        Self::response_biz_error(BizError::InternalError)
    }
}

impl<T> From<BizResult<T>> for ApiResponse<T> {
    fn from(req: BizResult<T>) -> ApiResponse<T> {
        match req {
            Ok(data) => ApiResponse::response_ok(data),
            Err(err) => ApiResponse::response_biz_error(err),
        }
    }
}
